{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Teil 9 - Einleitung in verschlüsselte Programme\n",
    "\n",
    "So komisch es auch klingen mag, es ist möglich auf verschlüsselten Daten zu rechnen. Mit anderen Worten: es ist möglich ein Programm zu entwickeln in dem **ALLE Variablen verschlüsselt** sind!\n",
    "\n",
    "In diesem Tutorial werden die grundlegenden Werkzeuge fürs verschlüsselte Rechnen behandelt. Im Speziellen wird der Ansatz des \"Secure Multi-Party Computation\" (Sicheres Rechnen mit mehreren Beteiligten) betrachtet.  \n",
    "In diesem Abschnitt wird eine Rechenmaschine entwickelt, welche mit verschlüsselten Zahlen umgehen kann. \n",
    "\n",
    "Autoren:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "- Théo Ryffel - GitHub: [@LaRiffle](https://github.com/LaRiffle)\n",
    "\n",
    "Referenzen: \n",
    "- Morten Dahl - [Blog](https://mortendahl.github.io) - Twitter: [@mortendahlcs](https://twitter.com/mortendahlcs)\n",
    "\n",
    "Übersetzer:\n",
    "- Jan Moritz Behnken - Github: [@JMBehnken](https://github.com/JMBehnken)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Schritt 1: Verschlüsseln mit Secure Multi-Party Computation\n",
    "\n",
    "SMPC ist auf den ersten Blick eine sonderbare Form der \"Verschlüsselung\". Anstelle eines öffentlichen/privaten Schlüssels für die Verschlüsselung der Variable, wird der Wert in mehrere `shares` aufgeteilt. Diese fungieren dann als private Schlüssel. Typischerweise werden diese `shares` an zwei oder mehrere _Besitzer_ verteilt. Somit müssen alle Besitzer der Variable einer Entschlüsselung zustimmen. Im Kern besitzt damit jeder einen privaten Schlüssel. \n",
    "\n",
    "### Verschlüsseln()\n",
    "\n",
    "Um nun die Variable `x` zu \"verschlüsseln\", kann folgendermaßen vorgegangen werden.\n",
    "\n",
    "> Die Verschlüsselung nutzt keinerlei Floats oder Reale Zahlen, sondern findet im mathematischen Raum des [Integer Quotientenringes](http://mathworld.wolfram.com/QuotientRing.html) statt. Dieser umfasst alle Integer zwischen `0` und `Q-1`, wobei `Q` eine Primzahl ist, welche \"groß genug\" ist, um alle Zahlen in den Berechnungen einzuschließen. In der Praxis wird ein gegebener Integer Wert `x` mit `x % Q` dem Ring angepasst. Aus diesem Grund sollten Zahlen `x' > Q'` vermieden werden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:37:17.256362Z",
     "start_time": "2020-04-03T16:37:17.251725Z"
    }
   },
   "outputs": [],
   "source": [
    "Q = 1234567891011"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:37:17.680089Z",
     "start_time": "2020-04-03T16:37:17.677526Z"
    }
   },
   "outputs": [],
   "source": [
    "x = 25"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:37:18.287488Z",
     "start_time": "2020-04-03T16:37:18.277729Z"
    }
   },
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "def encrypt(x):\n",
    "    share_a = random.randint(-Q,Q)\n",
    "    share_b = random.randint(-Q,Q)\n",
    "    share_c = (x - share_a - share_b) % Q\n",
    "    return (share_a, share_b,  share_c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:37:22.778759Z",
     "start_time": "2020-04-03T16:37:22.760012Z"
    }
   },
   "outputs": [],
   "source": [
    "encrypt(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Wie zu sehen ist, wurde die Variable `x` in drei unterschiedliche Anteile aufgespalten. Diese Anteile könnten an drei unterschiedliche Besitzer versendet werden.\n",
    "\n",
    "### Entschlüsseln()\n",
    "\n",
    "Sollen die drei Anteil entschlüsselt werden, so reicht es aus sie aufzusummieren und den Modulus zu berechnen (mod Q)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:39:41.042585Z",
     "start_time": "2020-04-03T16:39:41.035453Z"
    }
   },
   "outputs": [],
   "source": [
    "def decrypt(*shares):\n",
    "    return sum(shares) % Q"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:39:41.491818Z",
     "start_time": "2020-04-03T16:39:41.487943Z"
    }
   },
   "outputs": [],
   "source": [
    "a,b,c = encrypt(25)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:39:42.067359Z",
     "start_time": "2020-04-03T16:39:42.059365Z"
    }
   },
   "outputs": [],
   "source": [
    "decrypt(a, b, c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Anzumerken ist, dass ein Entschlüsseln mit nur zwei Anteilen nicht funktioniert!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:40:34.458388Z",
     "start_time": "2020-04-03T16:40:34.449682Z"
    }
   },
   "outputs": [],
   "source": [
    "decrypt(a, b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Somit werden alle Besitzer benötigt um den Wert entschlüsseln zu können. Genau deshalb verhalten sich die Anteile wie private Schlüssel, welche ebenfalls alle anwesend sein müssen, um eine Entschlüsselung zu ermöglichen."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Schritt 2: Grundlegende Arithmetik mit SMPC\n",
    "\n",
    "Die herausragende Eigenschaft von Secure Multi-Party Computation ist, dass **Berechnungen möglich sind während die Variablen verschlüsselt bleiben**. Simple Addition wird hier als Beispiel heran gezogen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:59:03.792433Z",
     "start_time": "2020-04-03T16:59:03.787160Z"
    }
   },
   "outputs": [],
   "source": [
    "x = encrypt(25)\n",
    "y = encrypt(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:59:06.712684Z",
     "start_time": "2020-04-03T16:59:06.704028Z"
    }
   },
   "outputs": [],
   "source": [
    "def add(x, y):\n",
    "    z = list()\n",
    "    # the first worker adds their shares together\n",
    "    z.append((x[0] + y[0]) % Q)\n",
    "    \n",
    "    # the second worker adds their shares together\n",
    "    z.append((x[1] + y[1]) % Q)\n",
    "    \n",
    "    # the third worker adds their shares together\n",
    "    z.append((x[2] + y[2]) % Q)\n",
    "    \n",
    "    return z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T16:59:07.239070Z",
     "start_time": "2020-04-03T16:59:07.230686Z"
    }
   },
   "outputs": [],
   "source": [
    "decrypt(*add(x,y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Erfolg!!!\n",
    "\n",
    "Es ist vollbracht! Wenn jeder Helfer für sich seine Anteile addiert, lassen sich die abgeänderten Anteile zum korrekten Ergebnis entschlüsseln (25 + 5 = 30).\n",
    "\n",
    "Wie sich herausstellte, können die SMPC Protokolle verwendet werden um folgende verschlüsselte Berechnungen zu ermöglichen:\n",
    "- Addition\n",
    "- Multiplikation\n",
    "- Vergleiche\n",
    "\n",
    "Und mit diesen grundlegenden Rechenarten, lässt sich jede erdenkliche Berechnung aufstellen!!!\n",
    "\n",
    "Im nächsten Abschnitt werden diese Operationen in der PySyft Bibliothek erläutert!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Schritt 3: SMPC mit PySyft\n",
    "\n",
    "In den vorangegangenen Abschnitten wurden die grundlegenden Berechnungen für SMPC vorgestellt. In der Praxis soll es jedoch nicht darum gehen diese Funktionen für eigene verschlüsselte Programme regelmäßig selbst implementieren zu müssen. Deshalb handelt dieser Abschnitt von der Umsetzung der verschlüsselten Berechnung in PySyft. Insbesondere wird es um die drei Grundlagen gehen:\n",
    "\n",
    "- addieren\n",
    "- multiplizieren\n",
    "- vergleichen\n",
    "\n",
    "Zuerst werden dafür einige virtuelle Helfer (bekannt aus früheren Tutorials) erstellt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:12:48.124033Z",
     "start_time": "2020-04-03T17:12:46.039419Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)\n",
    "\n",
    "bob = sy.VirtualWorker(hook, id=\"bob\")\n",
    "alice = sy.VirtualWorker(hook, id=\"alice\")\n",
    "bill = sy.VirtualWorker(hook, id=\"bill\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Grundlegendes Verschlüsseln/Entschlüsseln\n",
    "\n",
    "Fürs Verschlüsseln reicht es aus auf irgendeinem PySyft Tensor `.share()` aufzurufen.  \n",
    "Fürs Entschlüsseln genügt ein Aufruf von `.get()` auf der geteilten Variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:14:27.166225Z",
     "start_time": "2020-04-03T17:14:27.158348Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([25])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:14:28.045973Z",
     "start_time": "2020-04-03T17:14:28.037608Z"
    }
   },
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:14:28.717480Z",
     "start_time": "2020-04-03T17:14:28.693772Z"
    }
   },
   "outputs": [],
   "source": [
    "encrypted_x = x.share(bob, alice, bill)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:14:29.119530Z",
     "start_time": "2020-04-03T17:14:29.109349Z"
    }
   },
   "outputs": [],
   "source": [
    "encrypted_x.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Prüfen der verschlüsselten Werte\n",
    "\n",
    "Werden die Helfer Bob, Alice und Bill genauer betrachtet, so können die erstellten Anteile eingesehen werden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:15:58.512029Z",
     "start_time": "2020-04-03T17:15:58.503385Z"
    }
   },
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:15:59.122013Z",
     "start_time": "2020-04-03T17:15:59.113629Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([25]).share(bob, alice, bill)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:15:59.969772Z",
     "start_time": "2020-04-03T17:15:59.960834Z"
    }
   },
   "outputs": [],
   "source": [
    "# Bob's share\n",
    "bobs_share = list(bob._objects.values())[0]\n",
    "bobs_share"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:16:02.587034Z",
     "start_time": "2020-04-03T17:16:02.579063Z"
    }
   },
   "outputs": [],
   "source": [
    "# Alice's share\n",
    "alices_share = list(alice._objects.values())[0]\n",
    "alices_share"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:16:06.948029Z",
     "start_time": "2020-04-03T17:16:06.936922Z"
    }
   },
   "outputs": [],
   "source": [
    "# Bill's share\n",
    "bills_share = list(bill._objects.values())[0]\n",
    "bills_share"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Wenn nötig, lassen sich die Werte auf DIESELBE Art entschlüsseln wie oben!!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:16:53.282153Z",
     "start_time": "2020-04-03T17:16:53.274814Z"
    }
   },
   "outputs": [],
   "source": [
    "Q = x.child.field\n",
    "\n",
    "(bobs_share + alices_share + bills_share) % Q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Beim Aufrufen von `.share()` wurde der Tensor in drei Anteile aufgeteilt und anschließend auf alle Beteiligten verteilt!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Verschlüsselte Arithmetik\n",
    "\n",
    "Nun wird die Arithmetik auf den zugrundeliegenden Daten vorgestellt! Die API ist dabei so aufgebaut, dass sie genau wie bei normalen PyTorch Tensoren verwendet werden kann."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:19:34.093038Z",
     "start_time": "2020-04-03T17:19:34.081522Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([25]).share(bob,alice)\n",
    "y = torch.tensor([5]).share(bob,alice)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:19:34.657076Z",
     "start_time": "2020-04-03T17:19:34.644169Z"
    }
   },
   "outputs": [],
   "source": [
    "z = x + y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:19:35.106599Z",
     "start_time": "2020-04-03T17:19:35.099137Z"
    }
   },
   "outputs": [],
   "source": [
    "z = x - y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Verschlüsselte Multiplikation\n",
    "\n",
    "Für die Multiplikation wird ein weiterer Beteiligter benötigt, der verlässlich zufällige Zahlen bereitstellt (und keinen Beteiligten bevorzugt). Dieser Beteiligte wird \"Crypto Provider\" genannt. Für alle folgenden Fälle ist der \"Crypto Provider\" nur ein weiterer virtueller Helfer, hervorzuheben ist dabei jedoch, dass er zu keinem Zeitpunkt ein Besitzer irgendwelcher Anteile ist. Es ist darauf zu achten, dass der \"Crypto Provider\" verlässlich arbeiten muss, da die Berechnungen sonst angreifbar werden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:26:26.551967Z",
     "start_time": "2020-04-03T17:26:26.549496Z"
    }
   },
   "outputs": [],
   "source": [
    "crypto_provider = sy.VirtualWorker(hook, id=\"crypto_provider\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:26:27.094682Z",
     "start_time": "2020-04-03T17:26:27.082277Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)\n",
    "y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:26:31.131139Z",
     "start_time": "2020-04-03T17:26:31.101511Z"
    }
   },
   "outputs": [],
   "source": [
    "# multiplication\n",
    "\n",
    "z = x * y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Auch Matrixmultiplikation ist möglich."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:26:45.891851Z",
     "start_time": "2020-04-03T17:26:45.877824Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([[1, 2],[3,4]]).share(bob,alice, crypto_provider=crypto_provider)\n",
    "y = torch.tensor([[2, 0],[0,2]]).share(bob,alice, crypto_provider=crypto_provider)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:26:46.661222Z",
     "start_time": "2020-04-03T17:26:46.623148Z"
    }
   },
   "outputs": [],
   "source": [
    "# matrix multiplication\n",
    "\n",
    "z = x.mm(y)\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Verschlüsselte Vergleiche\n",
    "\n",
    "Es ist ebenfalls möglich vertrauliche Vergleiche mit verschlüsselten Daten durchzuführen. Dafür wird das SecureNN Protokoll verwendet, welches [hier](https://eprint.iacr.org/2018/442.pdf) genauer studiert werden kann. Das Ergebnis des Vergleiches ist erneut ein vertraulicher Tensor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:28:41.765613Z",
     "start_time": "2020-04-03T17:28:41.754424Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)\n",
    "y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:28:42.234041Z",
     "start_time": "2020-04-03T17:28:42.092760Z"
    }
   },
   "outputs": [],
   "source": [
    "z = x > y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:28:42.448586Z",
     "start_time": "2020-04-03T17:28:42.321796Z"
    }
   },
   "outputs": [],
   "source": [
    "z = x <= y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:28:43.154456Z",
     "start_time": "2020-04-03T17:28:43.013685Z"
    }
   },
   "outputs": [],
   "source": [
    "z = x == y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:28:45.670312Z",
     "start_time": "2020-04-03T17:28:45.522964Z"
    }
   },
   "outputs": [],
   "source": [
    "z = x == y + 20\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Auch die `max` Operation ist möglich."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:29:03.357377Z",
     "start_time": "2020-04-03T17:29:02.921663Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([2, 3, 4, 1]).share(bob,alice, crypto_provider=crypto_provider)\n",
    "x.max().get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-03T17:29:03.745499Z",
     "start_time": "2020-04-03T17:29:03.580741Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([[2, 3], [4, 1]]).share(bob,alice, crypto_provider=crypto_provider)\n",
    "max_values, max_ids = x.max(dim=0)\n",
    "max_values.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Herzlichen Glückwunsch!!! - Zeit, der Community beizutreten! \n",
    "\n",
    "Herzlichen Glückwunsch zum Abschluss dieses Notebook-Tutorials! Wenn es Ihnen gefallen hat und Sie sich der Bewegung zur Wahrung der Privatsphäre, zum dezentralisiertenen Besitz von KI und der KI-Lieferkette (Daten) anschließen möchten, können Sie dies auf folgende Weise tun! \n",
    "\n",
    "### PySyft auf GitHub einen Stern geben! \n",
    "\n",
    "Der einfachste Weg, unserer Community zu helfen, besteht darin, die GitHub-Repos mit Sternen auszuzeichnen! Dies hilft, das Bewusstsein für die coolen Tools zu schärfen, die wir bauen. \n",
    "\n",
    "- [Gib PySyft einen Stern](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Mach mit bei Slack! \n",
    "\n",
    "Der beste Weg, um über die neuesten Entwicklungen auf dem Laufenden zu bleiben, ist, sich unserer Community anzuschließen! Sie können dies tun, indem Sie das Formular unter [http://slack.openmined.org](http://slack.openmined.org) ausfüllen.\n",
    "\n",
    "### Treten Sie einem Code-Projekt bei! \n",
    "\n",
    "Der beste Weg, um zu unserer Community beizutragen, besteht darin, Entwickler zu werden! Sie können jederzeit zur PySyft GitHub Issues-Seite gehen und nach \"Projects\" filtern. Dies zeigt Ihnen alle Top-Level-Tickets und gibt einen Überblick darüber, an welchen Projekten Sie teilnehmen können! Wenn Sie nicht an einem Projekt teilnehmen möchten, aber ein wenig programmieren möchten, können Sie auch nach weiteren \"einmaligen\" Miniprojekten suchen, indem Sie nach GitHub-Problemen suchen, die als \"good first issue\" gekennzeichnet sind. \n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Spenden\n",
    "\n",
    "Wenn Sie keine Zeit haben, zu unserer Codebase beizutragen, aber dennoch Unterstützung leisten möchten, können Sie auch Unterstützer unseres Open Collective werden. Alle Spenden fließen in unser Webhosting und andere Community-Ausgaben wie Hackathons und Meetups! \n",
    "\n",
    " - [OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
